import * as fs from 'fs';
import { typeTpl } from '../templates';
import { nunjucks } from '../templates/nunjucks';
import {
  arrayToCamelCase,
  formatModelName,
  formatPropertyType,
  generateSvcName,
  log,
} from '../utils';

export class TypingGenerator {
  private path: string;

  constructor(jsonPath: string) {
    this.path = jsonPath;
  }

  start() {
    this.writeTyping();
  }

  // nodejs 读取文件
  public readJson() {
    const content = fs.readFileSync(this.path, 'utf-8');

    return JSON.parse(content);
  }

  // 读取definitions
  public readDefinitions() {
    const content = this.readJson();
    return content.definitions;
  }

  public readPaths() {
    const content = this.readJson();
    return content.paths;
  }

  // 净化definitions
  public formatDefinitions() {
    const definitions = this.readDefinitions();
    const keys = Object.keys(definitions);

    const result = keys.reduce((acc, cur) => {
      return {
        ...acc,
        [formatModelName(cur)]: definitions[cur],
      };
    }, {});

    return result;
  }

  // 生成模板数据
  public genTplData(model, name) {
    const { required = [], properties = {} } = model;
    const propertyKeys = Object.keys(properties);

    return {
      name,
      length: propertyKeys.length,
      properties: propertyKeys.map((key) => {
        const property = properties[key];
        const { type, items, originalRef } = property;
        let formatedType = formatPropertyType(type);

        if (formatedType === 'array') {
          if (items.type) {
            if (items.type === 'array') {
              formatedType = `${formatPropertyType(items.items.type)}[][]`;
            } else {
              formatedType = `${formatPropertyType(items.type)}[]`;
            }
          } else if (items.originalRef) {
            formatedType = `${formatModelName(items.originalRef)}[]`;
          }
        } else if (originalRef) {
          formatedType = `${formatModelName(originalRef)}`;
        }

        return {
          name: key,
          type: formatedType,
          description: property.description?.slice(0, 90),
          example: property.example ? `${property.example}`.slice(0, 20) : '',
          optional: required.length > 0 && !required.includes(key),
        };
      }),
    };
  }

  // 请求参数类型
  public genRequestParamsTypes() {
    const paths = this.readPaths();
    const keys = Object.keys(paths);

    const result = keys.reduce((acc, cur) => {
      const path = paths[cur];
      const { parameters = [], summary } = path.post || path.get;
      const params = parameters.filter((item) => item.name !== 'userId');

      if (params.length === 0) {
        return acc;
      }

      return [
        ...acc,
        {
          name: generateSvcName(cur, path.get ? 'TGet' : 'TPost') + 'Params',
          summary,
          length: params.length,
          properties: params.map((item) => {
            const { name, type, description, items, schema, required } = item;

            /** ====** ↓↓↓↓ 此段代码需根据具体项目接口文档进行调整 ↓↓↓↓ **==== */
            let propertyType = formatPropertyType(type);

            if (schema && schema.originalRef) {
              propertyType = formatModelName(schema.originalRef);
            }

            if (propertyType === 'array') {
              if (path.get) {
                propertyType = 'string';
              } else {
                propertyType = formatPropertyType(items.type) + '[]';
              }
            } else if (schema && schema.type) {
              if (schema.type === 'array') {
                propertyType = formatPropertyType(schema.items.type) + '[]';
              } else {
                propertyType = formatPropertyType(schema.type);
              }
            }

            /** ====** ↑↑↑↑ 此段代码需根据具体项目接口文档进行调整 ↑↑↑↑ **==== */

            return {
              name,
              type: propertyType,
              description,
              optional: !required,
              example: '',
            };
          }),
        },
      ];
    }, []);

    return result;
  }

  // 写typing文件
  public writeTyping(name?: string) {
    // 后端model
    const result = this.formatDefinitions();
    const start = `declare namespace API {\n`;
    const rKeys = Object.keys(result);
    const content = rKeys.reduce((acc, cur) => {
      const item = result[cur];
      const tplData = this.genTplData(item, cur);

      return acc + nunjucks.renderString(typeTpl, tplData, {});
    }, '');

    // 请求参数类型
    const svcTypes = this.genRequestParamsTypes();
    const svcContent = svcTypes.reduce((acc, cur) => {
      const tplData = cur;
      return acc + nunjucks.renderString(typeTpl, tplData);
    }, '');

    const end = `}\n`;

    fs.writeFileSync(`./api/api.d.ts`, start + content + svcContent + end);
    log('api.d.ts生成成功');
  }
}
